import * as React from "react";
import {CSSProperties} from "react";
import {renderItems, VJsonComponent} from "../../factory";
import {ComplexArrayResult, defaultMode, RefreshMode, VJsonDataComponentBase} from "../../datasource";
import _ from "lodash";
import * as mobx from 'mobx'
import {AgGridReact} from 'ag-grid-react'
import 'ag-grid-community/dist/styles/ag-grid.css'
import 'ag-grid-community/dist/styles/ag-theme-alpine.css'
import GridLocale from "./gridLocale";
import GridIdRender from './gridIdRender'
import {Alert, Button, Checkbox, Divider, Dropdown, Menu, Pagination as AntdPagination, Space} from "antd";
import {ApiObject} from "../../ajax";
import {applyValue, YvanEventDispatch} from "../../utils/helper";
import * as Extend from "../../extend";
import {
    ColumnWidthOutlined,
    DownloadOutlined,
    DownSquareOutlined,
    FullscreenExitOutlined,
    FullscreenOutlined,
    FunnelPlotOutlined,
    MergeCellsOutlined,
    ReloadOutlined,
} from '@ant-design/icons'
import './grid.css'
import {GridSelectCondition, GridState, GridVJsonDataProp, IGridCellRenderParam} from "./grid_type";
import CtlGridCellCheckbox from "./CtlGridCellCheckbox";
import CtlGridHeadCheckbox from "./CtlGridHeadCheckbox";
import CtlGridFilterSet from './CtlGridFilterSet'
import CellVJsonRenderer from './CellVJsonRenderer'
import CheckboxVJsonRender from "./CheckboxVJsonRender";
import {ColumnApi, GridApi} from "ag-grid-community";
import CheckboxVJsonHeaderRender from "./CheckboxVJsonHeaderRender";
import {XLSX_EXPORT} from "../../xlsx";
import SelectEditor from "./SelectEditor";
import DatePickerEditor from "./DatePickerEditor";
import {showInfo} from "../notify";

interface WidthData {
    __ID__?: number,
    cols: {
        colId: string,
        width: number
    }[],
}

@VJsonComponent({
    view: 'grid'
})
export default class Grid extends VJsonDataComponentBase<Grid, GridVJsonDataProp, GridState> {
    static defaultProps = {
        ...VJsonDataComponentBase.defaultProps,
        pagination: true,
        showRowNumber: true,
        showHeader: true,
        valueSep: ',',
        bindSelected: '',
        height: 'auto',
        className: '',
        columns: [],
        toolbar: [],
        hideToolbar: false,
        fullscreen: false,
        filterable: false,
        hideToolbarDivider: false,
        hideRefreshButton: false,
        hideExportButton: false,
        hideAutoFitButton: false,
        hideAutoSizeButton: false,
        hideColumnSettingButton: false,
        hideFullScreenButton: false,
        exportExcelName: 'exportExcel.xlsx',
        paginationOption: {
            size: "small",
            defaultPageSize: 10,
            pageSizeOptions: ['10', '20', '50', '100'],
            showSizeChanger: true,
            showQuickJumper: true,
            showTotal: (total: number) => `总共 ${total} 条`
        },
        notSaveColSetting: false,
        onExportExcelStyle: undefined,
        onExportExcelCellBgColor: undefined
    }

    /**
     * 是否第一次 onGridReady
     */
    private _isFirstRender: boolean = true

    // 不需要基类的 autoRefresh
    protected isOverwriteAutoRefresh = true

    /**
     * bindSelected 双向绑定释放器
     */
    private _bindSelectDisposer: any

    /**
     * 用来记录第一个 checkbox 列的勾选状态
     */
    private _checkboxColumn?: string = undefined

    private grid!: AgGridReact
    public gridApi!: GridApi
    public columnApi!: ColumnApi
    private _data: any = []
    private _exportData: any = []
    private _loading: boolean = false
    private _gridOptionsResult: any
    private _gridEditCells: any

    @mobx.observable
    private mobxState: {
        needCount: boolean
        pageSize: number
        currentPage: number
        total: number
        columnHiddenSetting: any
    } = {
        needCount: true,
        pageSize: 10,
        currentPage: 1,
        total: 0,
        columnHiddenSetting: {},
    }

    /**
     * 数据版本，每次 set data() 之后，势必会造成 dataVersion 更改
     * autorun 的方法方便监控
     */
    dataVersion = mobx.observable.box(_.uniqueId('v_'))

    checkedRows: any[] = []

    allCheckedCallBack:any

    constructor(props: any) {
        super(props);
        if (!this.props.vjson.notSaveColSetting) {
            this.mergeLocalColWidth(props);
        }

        const me = this;
        _.defaults(
            props.vjson,
            Grid.defaultProps
        )
        this.allCheckedCallBack = props.vjson.allCheckedCallBack
        // 换掉了基类的 onDataSourceAfter 方法，替换为 dataSourceAfterSuper
        this.dataSourceAfterSuper = this.onDataSourceAfter
        this.onDataSourceAfter = this.dataSourceAfter

        // 设置默认 state
        this.mobxState.pageSize = props.vjson.paginationOption.defaultPageSize

        this._gridOptionsResult = this._gridOptions()
    }

    private readonly dataSourceAfterSuper?: (sender: Grid, dataOrgin: any[], responseRaw: any, api: ApiObject, param?: RefreshMode) => ComplexArrayResult<any>

    componentWillUnmount() {
        super.componentWillUnmount();
        if (!this.props.vjson.notSaveColSetting) {
            this.saveColWidth();
        }
        if (typeof this._bindSelectDisposer === 'function') {
            this._bindSelectDisposer()
        }
    }

    /**
     * 获取被选中的行数据
     */
    getSelectedRow() {
        const rows = this.getSelectedRows()
        return rows.length > 0 ? rows[0] : undefined
    }

    /**
     * 获取当前焦点所在单元格
     */
    getFocusedCell(): undefined | { rowIndex: number, column: any } {
        return this.gridApi.getFocusedCell()
    }

    /**
     * 获取当前焦点所在行的数据内容
     */
    getFocusedRow(): undefined | object {
        const cell = this.gridApi.getFocusedCell()
        if (cell) {
            const {rowIndex} = cell
            const {data} = this.gridApi.getDisplayedRowAtIndex(rowIndex)
            return data
        }
    }

    /**
     * 获取被选中的行数据(多选情况下回返回多行数据)
     */
    getSelectedRows() {
        // 调用原生 getSelectedRows 方法有 bug
        // return this.gridApi.getSelectedRows();
        const selected: any[] = []
        this._findNode((node: any) => {
            if (node.selected) {
                selected.push(node.data)
            }
            return false
        })
        return selected
    }

    getCheckedRows() {
        return this.checkedRows
    }

    _refreshCheckedRows(data: any[]) {
        for (let i = 0; i < this.checkedRows.length; i++) {
            const item = this.checkedRows[i]
            for (let j = 0; j < data.length; j++) {
                const newItem = data[j]
                if (this._getIdByRow(item) == this._getIdByRow(newItem)) {
                    _.assign(item, newItem)
                }
            }
        }
    }

    /**
     * 获取所有编辑之后的数据
     */
    getGridEditedRows() {
        const editedRows: any[] = []
        this._findNode((node: any) => {
            if (node.origin) {
                if (!_.isEqual(node.data, node.origin)) {
                    editedRows.push(node.data)
                }
            }
            return false
        })
        return editedRows
    }

    createNewRow(newRowData?: any) {
        this.data = [
            ...this.data,
            {
                __grid_row_new: true,
                ...newRowData
            }
        ]
    }

    deleteRow(rowData: any) {
        this.data = _.filter(this.data, item => {
            return !_.isEqual(item, rowData)
        })
    }

    deleteAllRows() {
        this.data = []
    }

    /**
     * 根据行，获取主键 ID, 当主键不存在时 返回 undefined
     */
    _getIdByRow(row: any) {
        if (!row) return
        if (this.props.vjson.idField) {
            if (_.isArray(this.props.vjson.idField)) {
                //联合组件，用valueSep分割
                return _.map(this.props.vjson.idField, (f: any) => _.toString(row[f])).join(this.props.vjson.valueSep)
            }

            return row[this.props.vjson.idField]
        }
        console.error('表格没有设置主键！！')
        return undefined
    }

    getRowById(id: any) {
        for (let i = 0; i < this.data.length; i++) {
            const item = this.data[i]
            if (item[this.props.vjson.idField] === id) {
                return item
            }
        }
        return null
    }

    /**
     * 循环当前的表格，设置表格中的数据，并刷新
     */
    forEachData(func: (row: any) => void) {
        this.gridApi.forEachNode((node: any) => {
            func.call(this, node.data)
        })
        this.gridApi.refreshCells({force: true});
    }

    /**
     * 找到某行的 node, （一旦匹配到 condition 就停止）
     */
    _findNode(condition: GridSelectCondition) {
        if (!condition) {
            //返回第一条被找到的数据
            condition = () => true
        } else if (typeof condition === 'string' || typeof condition === 'number') {
            //以主键查找的方式
            const key = condition
            condition = (n: any) => {
                return this._getIdByRow(n.data) === key
            }
        } else if (typeof condition === 'object') {
            //就是 node 对象, 直接返回
            return condition
        }

        const me = this
        let findNode = undefined
        try {
            this.gridApi.forEachNode((node: any) => {
                if ((condition as any).call(me, node)) {
                    findNode = node
                    throw Error()
                }
            })
        } catch (e) {
        }
        return findNode
    }

    /**
     * 获取被选中的行(多选情况下回返回多行)
     */
    getSelectedNodes() {
        // 调用原生 getSelectedRows 方法有 bug
        // return this.gridApi.getSelectedRows();
        const selected: any[] = []
        this._findNode((node: any) => {
            if (node.selected) {
                selected.push(node)
            }
            return false
        })
        return selected
    }

    /**
     * 获取被选中的行
     */
    getSelectedNode() {
        const nodes = this.getSelectedNodes()
        return nodes.length > 0 ? nodes[0] : undefined
    }

    /**
     * 获取被选中的行主键
     */
    getSelectedId() {
        const row = this.getSelectedRow()
        if (row) {
            return this._getIdByRow(row)
        }
    }

    /**
     * 自定义 serverJS Data 参数
     */
    protected serverJsParamsParser(apiObject: ApiObject, param: RefreshMode = defaultMode()): Promise<ApiObject> {
        const me = this;
        return new Promise(resolve => {
            const pageOption: any = {}
            if (this.props.vjson.pagination) {
                const pageSize = this.mobxState.pageSize
                const currentPage = (this.mobxState.currentPage > 0 && param.resetPage === false) ? this.mobxState.currentPage - 1 : 0

                // 需要分页
                _.assign(pageOption, {
                    limit: pageSize,
                    limitOffset: currentPage * pageSize,
                    needCount: currentPage === 0 || me.mobxState.needCount
                })
                if (apiObject.method === 'invoke') {
                    pageOption.page = currentPage + 1
                }
            }
            me.mobxState.needCount = true;
            // @ts-ignore
            if (apiObject.method === 'server') {
                // 表格的标准请求为：
                //      filterModel: {}
                //      limit: 100
                //      limitOffset: 0
                //      needCount: true
                //      params: {…}
                //      sortModel: []

                const params = apiObject.data
                apiObject.data = {
                    filterModel: this.gridApi.getFilterModel(),
                    params,
                    sortModel: this.gridApi.getSortModel()
                }

                // console.log('业务代码写在这里', apiObject.data)
                _.assign(apiObject.data, pageOption)

            } else if (apiObject.method === 'invoke') {
                const params = apiObject.data
                apiObject.data = {
                    filterModel: this.gridApi.getFilterModel(),
                    ...params,
                    sortModel: this.gridApi.getSortModel()
                }
                // console.log('业务代码写在这里', apiObject.data)
                _.assign(apiObject.data, pageOption)
                apiObject.data = [apiObject.data]
            } else {
                // 自主性 ajax 只是添加一个 pagination 属性
                _.set(apiObject, 'pagination', pageOption)
            }

            if (param.isExportData === true) {
                _.set(apiObject, 'data.isExportData', true);
                _.set(apiObject, 'data.exportDataCount', me.props.vjson.exportDataCount)
            }
            resolve(apiObject)
        })
    }

    /**
     * 拿到原始的请求结果之后，需要进行下一步处理
     */
    private dataSourceAfter(sender: Grid, dataOrgin: any[], responseRaw: any, api: ApiObject, param: RefreshMode = defaultMode()): ComplexArrayResult<any> {
        const dataSourceAfterSuper = this.dataSourceAfterSuper || ((_, dataOrgin: any, responseRaw: any) => responseRaw)
        this._refreshCheckedRows(dataOrgin);
        const me = this
        // 换掉了基类的 onDataSourceAfter 方法，要重新调用回来
        return new Promise(resolve => {
            applyValue(dataSourceAfterSuper.call(this, sender, dataOrgin, responseRaw, api, param)).then(res => {

                if (typeof res === 'undefined' || res === null) {
                    // 忽略或清空结果
                    return resolve(res)
                }

                if (param.isExportData) {
                    if (typeof res.pagination === 'object') {
                        // @ts-ignore
                        resolve(res.data)
                    } else {
                        // @ts-ignore
                        resolve(res)
                    }
                    return
                }
                if (this.props.vjson.pagination) {
                    if (typeof res.pagination === 'object') {
                        // 从 res 中能获取到 pagination
                        if (typeof res.pagination.total !== 'undefined') {
                            // 需要改变 total 数量
                            if (typeof res.pagination.current === 'undefined') {
                                // 只改变总数
                                mobx.transaction(() => {
                                    // @ts-ignore
                                    me.mobxState.total = ((!api) || (api && api.data && api.data.needCount === true) ? parseInt(res.pagination.total) : sender.mobxState.total)
                                })

                            } else {
                                // 改变总数、当前页号、页大小
                                mobx.transaction(() => {
                                    me.mobxState.currentPage = parseInt(res.pagination.current)
                                    // @ts-ignore
                                    me.mobxState.total = ((!api) || (api && api.data && api.data.needCount === true) || (api && api.data && api.data[0]?.needCount === true) ? parseInt(res.pagination.total) : sender.mobxState.total)
                                    me.mobxState.pageSize = parseInt(res.pagination.size)
                                })
                            }
                        }

                        // @ts-ignore
                        resolve(res.data)

                    } else {
                        // 处理分页数据
                        // mobx.transaction(() => {
                        //     me.mobxState.currentPage = parseInt(responseRaw.pagination.current)
                        //     me.mobxState.total = (parseInt(responseRaw.pagination.total) === 0 ? sender.mobxState.total : parseInt(responseRaw.pagination.total))
                        //     me.mobxState.pageSize = parseInt(responseRaw.pagination.size)
                        // })
                        mobx.transaction(() => {
                            me.mobxState.currentPage = 1
                            me.mobxState.total = 0
                        })

                        // @ts-ignore
                        resolve(res)
                    }
                } else {
                    resolve(res)
                }
            })
        })
    }

    /**
     * 变换列定义, 生成最终适合的AgGrid的列定义
     * param columns
     */
    private _gridCols(columns: any[]) {

        let colHiddenData = this.getSavedColumnHidden();

        _.forEach(mobx.toJS(this.props.vjson.columns), (column) => {
            const col: any = {
                ...column,
                headerName: column.title,
                suppressMovable: true,
                hide: column.hidden,
                cellStyle: (param: any) => {
                    return YvanEventDispatch(column.onStyle, this, {
                        "field": param.colDef.field,
                        "value": param.value,
                        "data": param.data
                    })
                }
            }

            if (col.onCellClicked) {
                col.onCellClicked = (param: any) => {
                    return YvanEventDispatch(column.onCellClicked, this, {
                        "field": param.colDef.field,
                        "value": param.value,
                        "data": param.data,
                        "rowIndex": param.rowIndex,
                        "column": param.column,
                    })
                }
            }

            if (col.sortable) {
                // 走服务端排序，客户端排序可以让其无效
                col.comparator = () => {
                    return 0;
                }
            }

            if (_.size(col.field) > 0) {
                col.tooltipField = col.field
            }

            // 记录列隐藏的设置
            if (col.field && !col.hidden) {
                this.mobxState.columnHiddenSetting[col.field] = col.hide
            }
            if (col.field && colHiddenData[col.field]) {
                this.mobxState.columnHiddenSetting[col.field] = colHiddenData[col.field];
            }

            let theFormatter: any
            if (typeof col.render === 'function') {
                //自定义渲染器
                _.defaults(col, {
                    cellRenderer: 'cellVJsonRenderer',
                    cellRendererParams: {
                        $default: this.props.$default,
                        $context: this.props.$context,
                        grid: this
                    }
                });

            } else if (col.view === 'checkbox') {
                // 勾选列
                _.defaults(col, {
                    colId: _.uniqueId("_"),
                    width: 40,
                    minWidth: 40,
                    maxWidth: 40,
                    align: 'center',
                    pinned: 'left',
                    resizable: false,
                    sortable: false,
                    suppressAutoSize: true,
                    suppressSizeToFit: true,
                    cellRenderer: 'checkboxVJsonRender',
                    headerComponent: 'checkboxVJsonHeaderRender',
                    cellRendererParams: {
                        $default: this.props.$default,
                        $context: this.props.$context,
                        grid: this
                    },
                    headerComponentParams: {
                        grid: this
                    }
                })
            } else if (typeof col.data === 'function' || _.isArray(col.data) || _.isPlainObject(col.data)) {
                // 带 data 属性的列, 能自动带入 filter / formatter / editMode 选项
                const data = (typeof col.data === 'function' ? col.data.call(this) : col.data)
                theFormatter = data
            }

            if (typeof col.formatter === 'string') {
                // formatter 是字符串，从全局 YvanUI.formatter 找方法
                if (!Extend.formatter.hasOwnProperty(col.formatter)) {
                    console.error('没有发现全局函数 YvanUI.formatter.' + col.formatter)
                } else {
                    theFormatter = Extend.formatter[col.formatter]
                }

            } else if (typeof col.formatter === 'function' || _.isArray(col.formatter) || _.isPlainObject(col.formatter)) {
                theFormatter = col.formatter
            }

            if (typeof theFormatter === 'function') {
                //formatter 是函数，调用函数来显示
                col.valueFormatter = (params: any) => {
                    if (_.size(params.data) <= 0) return undefined
                    return theFormatter.call(this, params.value, this, params.data)
                }
            } else if (_.isArray(theFormatter)) {
                //formatter 是数组，取 id/text 属性作为映射关系
                col.valueFormatter = (param: any) => {
                    let displayValue = param.value
                    _.some(theFormatter, ({id, text}) => {
                        const r = (id && _.trim(id) === _.trim(param.value))
                        if (r) {
                            displayValue = text
                            return true
                        }
                    })
                    return displayValue
                }

            } else if (_.isPlainObject(theFormatter)) {
                //formatter 是对象，根据 key 取映射关系
                col.valueFormatter = (param: any) => {
                    if (_.has(theFormatter, _.trim(param.value))) {
                        return theFormatter[_.trim(param.value)]
                    }
                }
            }

            col.cellClass = (params: any) => {
                return 'cell-' + col.align
            }

            delete col.hidden;
            delete col.title;

            //=========================== 过滤属性 ===========================
            if (this.props.vjson.filterable && col.filterable && !col.hidden) {
                const datas = col.data
                if (typeof datas === 'object') {
                    //下拉框过滤
                    _.assign(col, {
                        filter: 'CtlGridFilterSet',
                        //suppressMenu: true,
                        filterParams: {
                            data: datas,
                        },
                    })

                } else if (col.type === 'number') {
                    //数字过滤
                    _.assign(col, {
                        filter: 'agNumberColumnFilter', //NumberFilter = 26850
                        //suppressMenu: true,
                        filterParams: {
                            applyButton: true,
                            clearButton: true,
                            suppressAndOrCondition: true,
                            filterOptions: [
                                // 服务器已经设置条件，浏览器不进行实际比对
                                {
                                    displayKey: 'equals', displayName: '等于', test() {
                                        return true;
                                    }
                                },
                                {
                                    displayKey: 'notEqual', displayName: '不等于', test() {
                                        return true;
                                    }
                                },
                                {
                                    displayKey: 'lessThan', displayName: '小于', test() {
                                        return true;
                                    }
                                },
                                {
                                    displayKey: 'greaterThan', displayName: '大于', test() {
                                        return true;
                                    }
                                },
                                {
                                    displayKey: 'lessThanOrEqual', displayName: '小于等于', test() {
                                        return true;
                                    }
                                },
                                {
                                    displayKey: 'greaterThanOrEqual', displayName: '大于等于', test() {
                                        return true;
                                    }
                                },
                                {
                                    displayKey: 'inRange', displayName: '范围', test() {
                                        return true;
                                    }
                                },
                            ]
                        }
                    })

                } else if (col.type === 'date' || col.type === 'datetime') {
                    //日期筛选
                    _.assign(col, {
                        filter: 'agDateColumnFilter',
                        filterParams: {
                            applyButton: true,
                            clearButton: true,
                            filterOptions: ['inRange'],
                            suppressAndOrCondition: true,
                            inRangeInclusive: true,
                            comparator(v1: any, v2: any) {
                                // 服务器已经设置条件，浏览器不进行实际比对
                                // 0 的情况，一定要包含 inRangeInclusive 条件
                                return 0
                            }
                        }
                    })
                } else {
                    //其他情况都是字符串筛选
                    _.assign(col, {
                        filter: 'agTextColumnFilter',
                        filterParams: {
                            applyButton: true,
                            clearButton: true,
                            filterOptions: ['contains', 'equals', 'startsWith'],
                            suppressAndOrCondition: true,
                            textCustomComparator() {
                                // 服务器已经设置条件，浏览器不进行实际比对
                                return true;
                            }
                        }
                    })
                }
            }

            //=========================== 编辑属性 ===========================
            if (col.editable) {
                switch (col.editMode) {
                    case 'select':
                        col.cellEditor = 'selectEditor'
                        break
                    case 'date':
                        col.cellEditor = 'datePickerEditor'
                        break
                }

                delete col.editMode;
            }
            col.instanceScope = this;
            columns.push(col)
        })
    }

    /** 设置行号, 兼容分页 **/
    private setRowId(p: any) {
        return GridIdRender(p, this)
    }

    /**
     * 生成表格定义 gridOptions
     */
    private _gridOptions(): any {

        const resultCols: any[] = []

        //显示序号列
        if (this.props.vjson.showRowNumber) {
            resultCols.push({
                field: '__ID__',
                headerName: GridLocale.rownumber,
                width: this.idColWidth || 52,
                //minWidth: 52,
                maxWidth: 160,
                pinned: 'left',
                resizable: true,
                sortable: false,
                suppressAutoSize: true,
                suppressSizeToFit: true,
                cellRenderer: 'CtlGridIdRender'
            })
        }

        //添加自定义列
        this._gridCols(resultCols)

        const gridOptions: any = {
            columnDefs: resultCols,
            onGridReady: this._onGridReady.bind(this),
            rowModelType: 'clientSide',
            animateRows: false,
            headerHeight: this.props.vjson.headerHeight,
            rowHeight: this.props.vjson.rowHeight,
            localeText: GridLocale,
            domLayout: (this.props.vjson.height === 'auto' ? 'autoHeight' : 'normal'),
            rowSelection: 'single',
            enableBrowserTooltips: true,
            suppressColumnMoveAnimation: true,
            singleClickEdit: true,
            suppressRowHoverHighlight: true,
            floatingFilter: false,
            enterMovesDown: false,
            enterMovesDownAfterEdit: false,
            accentedSort: true,
            rowData: this._data,
            defaultColDef: {
                // flex: 1,
                wrapText: true,
                autoHeight: true,
                sortable: true,
                resizable: true,
            },
            onColumnResized: (params: any) => {
                params.api.resetRowHeights()
            },
            onColumnVisible: (params: any) => {
                params.api.resetRowHeights()
            },
            onRowSelected: this._rowSelected.bind(this),
            onCellClicked: this._onCellClicked.bind(this),
            onRowDoubleClicked: this._rowDoubleClicked.bind(this),
            onSortChanged: this._sortChanged.bind(this),
            onFilterChanged: this._filterChanged.bind(this),
            onCellKeyDown: this._cellKeyDown.bind(this),
            onCellEditingStopped: this._cellEditingStopped.bind(this),
            onCellEditingStarted: this._cellEditingStarted.bind(this),
            components: {
                CtlGridFilterSet: CtlGridFilterSet,
                CtlGridCellCheckbox: CtlGridCellCheckbox,
                CtlGridHeadCheckbox: CtlGridHeadCheckbox,
                CtlGridIdRender: this.setRowId.bind(this)
            },
            frameworkComponents: {
                checkboxVJsonRender: CheckboxVJsonRender,
                checkboxVJsonHeaderRender: CheckboxVJsonHeaderRender,
                cellVJsonRenderer: CellVJsonRenderer,
                selectEditor: SelectEditor,
                datePickerEditor: DatePickerEditor,
            }
        }

        if (this.props.vjson.rowStyle) {
            gridOptions.rowStyle = this.props.vjson.rowStyle
        }
        if (this.props.vjson.rowClass) {
            gridOptions.rowClass = this.props.vjson.rowClass
        }
        if (this.props.vjson.getRowStyle) {
            gridOptions.getRowStyle = this.props.vjson.getRowStyle
        }
        if (this.props.vjson.getRowClass) {
            gridOptions.getRowClass = this.props.vjson.getRowClass
        }
        if (!this.props.vjson.showHeader) {
            gridOptions.headerHeight = 0
        }

        if (mobx.has(this.props.vjson, 'wrapText')) {
            gridOptions.defaultColDef.wrapText = this.props.vjson.wrapText
        }
        if (mobx.has(this.props.vjson, 'suppressRowClickSelection')) {
            gridOptions.suppressRowClickSelection = this.props.vjson.suppressRowClickSelection
        }
        if (mobx.has(this.props.vjson, 'suppressCellSelection')) {
            gridOptions.suppressCellSelection = this.props.vjson.suppressCellSelection
        }
        if (mobx.has(this.props.vjson, 'suppressRowHoverHighlight')) {
            gridOptions.suppressRowHoverHighlight = this.props.vjson.suppressRowHoverHighlight
        }

        return gridOptions;
    }


    set data(value: any[]) {
        this._data = value
        if (this.gridApi) {
            this.gridApi.setRowData(this._data)
            this.dataVersion.set(_.uniqueId('v_'))
        }
        if (this.onDataSourceFinish) {
            this.onDataSourceFinish(this)
        }
    }

    get data() {
        return this._data
    }

    set refreshLessData(value: any[]) {
        this._data = value
        if (this.gridApi) {
            this._transactionUpdateRow(this._data);
        }
    }

    set loading(value: boolean) {
        this._loading = value
        if (this.gridApi) {
            if (value) {//显示
                //this.gridApi.showLoadingOverlay();
                let list = `<div class="grid-mask">
                <div class="ant-spin ant-spin-lg ant-spin-spinning"><span class="ant-spin-dot ant-spin-dot-spin"><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i></span>
                </div></div>`
                $('div[view_id=' + this.key + ']').append(list);
                $('div[view_id=' + this.key + '] .ag-overlay-panel').hide();

            } else {//隐藏
                //this.gridApi.hideOverlay()
                $('div[view_id=' + this.key + '] .grid-mask').remove();
                $('div[view_id=' + this.key + '] .ag-overlay-panel').show();
            }
        }
    }

    get loading() {
        return this._loading
    }

    refreshData() {
        this.refresh({resetPage: false});
    }

    /**
     * 无感知刷新
     */
    // _transactionUpdate(targetDataList: any[]) {
    //
    //     if (this.useCrossPageCheck) {
    //         this._validateRowChecked(targetDataList);
    //     }
    //
    //     if (this.editable) {
    //         const newRow = _.assign({
    //             __grid_row_new: true
    //         }, this.vjson.defautProperties)
    //         targetDataList.push(newRow)
    //         const maxLength: any = _.max([this.pageSize, targetDataList.length])
    //         this.paginationSetPageSize(maxLength + 1)
    //     }
    //
    //     this._gridData = targetDataList;
    //
    //     if (this.refreshMode === GridRefreshMode.refreshWithFilter || this.refreshMode === GridRefreshMode.refreshAndClearFilter) {
    //
    //         /** 更改当前的刷新模式， 避免重复刷新 **/
    //         this.refreshMode = GridRefreshMode.refreshRows
    //         this.setData(targetDataList);
    //         if (this.dataSourceBind) {
    //             this.gridApi.setFilterModel(this.dataSourceBind.lastFilterModel)
    //         }
    //     } else {
    //         this._transactionUpdateRow(targetDataList)
    //     }
    // }

    /**
     * 刷新行数据
     */
    _transactionUpdateRow(targetDataList: any[]) {
        /** 更改当前的刷新模式， 避免重复刷新 **/
            // this.refreshMode = GridRefreshMode.refreshRows
        const transaction: any = {
                add: [],
                remove: [],
                update: []
            }
        let i = 0
        this.gridApi.forEachNode((node: any) => {
            if (i === targetDataList.length) {
                //已经越位
                transaction.remove.push(node.data)
            } else {
                const newData = targetDataList[i++]
                node.setData(newData)
                transaction.update.push(node.data)
            }
        })
        for (; i < targetDataList.length; i++) {
            transaction.add.push(targetDataList[i])
        }
        this.gridApi.updateRowData(transaction)
    }

    /**=========================== 表格行列定位 ======================== **/

    /**
     * 选择一个指定行
     * @param condition 可以是 key(主键), 也可以是 function 条件, 匹配到符合 condition 的第一条记录
     */
    selectRow(condition: GridSelectCondition) {
        const node: any = this._findNode(condition)
        if (node) {
            this.gridApi.setFocusedCell(node.rowIndex, '__ID__')
            node.setSelected(true)
            return true
        }
        return false
    }

    /**
     * 选择一个指定行
     * @param condition 可以是 key(主键), 也可以是 function 条件, 匹配到符合 condition 的第一条记录
     */
    singleSelectRow(condition: GridSelectCondition) {
        const node: any = this._findNode(condition)
        if (node) {
            node.setSelected(true)
            return true
        }
        return false
    }

    /**
     * 选择一个指定行
     * @param condition
     */
    selectRowWithCell(condition: GridSelectCondition, cellField: string) {
        const node: any = this._findNode(condition)
        if (node) {
            this.gridApi.setFocusedCell(node.rowIndex, cellField)
            node.setSelected(true)
            return true
        }
        return false
    }

    /**
     * 闪烁指定行
     * @param condition 可以是 key(主键), 也可以是 function 条件, 匹配到符合 condition 的第一条记录
     */
    flashRow(condition: any) {
        const node = this._findNode(condition)
        if (node) {
            this.gridApi.flashCells({rowNodes: [node]})
            return true
        }
        return false
    }

    /**
     * 闪烁指定单元格
     * @param cols 列数组
     * @param condition 可以是 key(主键), 也可以是 function 条件, 匹配到符合 condition 的第一条记录
     */
    flashCell(cols: any, condition: any) {
        const node = this._findNode(condition)
        if (node) {
            this.gridApi.flashCells({columns: cols, rowNodes: [node]})
            return true
        }
        return false
    }

    /**=========================== 表格事件相关 ======================== **/

    private _rowSelected(param: any) {
        const {node} = param
        const {selected, id} = node

        if (!selected) {
            //行离开事件,查看是否有数据正在编辑，提交校验
            return
        }

        //触发 bindSelected 改变
        if (this.props.vjson.bindSelected) {
            const scope = this.props.$context.scopeInstance
            _.set(scope, this.props.vjson.bindSelected, param.data)
        }

        //触发 onRowSelect 事件
        YvanEventDispatch(this.props.vjson.onRowSelect, this, param.data)
    }

    private _sortChanged() {
        this.refresh()
    }

    private _filterChanged() {
        this.refresh()
    }

    _saveOriginRowData(param: any) {
        if (!param.node.origin) {
            // 以前从来没有编辑过这一行, 记录 origin
            const data = _.cloneDeep(param.data)
            delete data['__ID__']
            param.node.origin = data
        }
    }

    _cellEditingStarted(param: any) {
        this._saveOriginRowData(param)
    }

    private _cellEditingStopped(param: IGridCellRenderParam) {

        YvanEventDispatch(this.props.vjson.onCellEditingStopped, this, param)

        if (!param.data) {
            return
        }

        // 结束编辑时，自动变换 bindSelected 和 view:checkbox 的 bind 内容
        const scope = this.props.$context.scopeInstance

        const currentId = this._getIdByRow(param.data)
        if (this.getSelectedId() === currentId) {
            _.set(scope, this.props.vjson.bindSelected, param.data)
        }

        _.some(this.columnApi.getAllColumns(), column => {
            const def: any = column.getColDef()
            if (def.view === 'checkbox' && def.bind) {
                _.forEach(_.get(scope, def.bind), (row) => {
                    if (this._getIdByRow(row) === currentId) {
                        _.extend(row, param.data)
                    }
                })
                return true
            }
        })
    }

    private _cellKeyDown(param: any) {
        const KEY_LEFT = 37
        const KEY_UP = 38
        const KEY_RIGHT = 39
        const KEY_DOWN = 40
        const KEY_TAB = 9
        const KEY_DELETE = 8
        const KEY_ENTER = 13
        const KEY_ESC = 27
        //event.stopPropagation();
        //event.preventDefault();

        //通知外部
        const r = YvanEventDispatch(this.props.vjson.onKeyDown, this, param)
        if (r === true) {
            //已经被自定义函数处理掉
            return
        }

        if (param.event.keyCode === KEY_ENTER) {
            param.event.stopPropagation()
            param.event.preventDefault()

        } else if (param.event.keyCode === KEY_DELETE) {

        } else if (param.event.keyCode === KEY_ESC) {

        } else if (param.event.keyCode === KEY_UP) {
            if (_.indexOf(this._gridEditCells, param.colDef.field) != -1 && this.gridApi.getEditingCells().length > 0) {
                const vv = param.rowIndex - 1
                if (vv < 0) {
                    return
                }
                param.event.stopPropagation()
                param.event.preventDefault()
                this.selectRowWithCell(node => node.rowIndex === vv, param.colDef.field)
                this.gridApi.stopEditing()
                this.gridApi.startEditingCell({
                    rowIndex: vv,
                    colKey: param.colDef.field
                })
            }
        } else if (param.event.keyCode === KEY_DOWN) {
            if (_.indexOf(this._gridEditCells, param.colDef.field) != -1 && this.gridApi.getEditingCells().length > 0) {
                const vv = param.rowIndex + 1
                if (vv >= this._data.length) {
                    return
                }
                param.event.stopPropagation()
                param.event.preventDefault()
                this.selectRowWithCell(node => node.rowIndex === vv, param.colDef.field)
                this.gridApi.stopEditing()
                this.gridApi.startEditingCell({
                    rowIndex: vv,
                    colKey: param.colDef.field
                })
            }
        } else if (param.event.keyCode === KEY_LEFT) {
            if (this.gridApi.getEditingCells().length > 0) {
                const vv = this._getPreviousEditCellFields(param.colDef.field)
                if (vv) {
                    param.event.stopPropagation()
                    param.event.preventDefault()
                    this.gridApi.setFocusedCell(param.rowIndex, vv)
                    this.gridApi.stopEditing()
                    this.gridApi.startEditingCell({
                        rowIndex: param.rowIndex,
                        colKey: vv
                    })
                }
            }
        } else if (param.event.keyCode === KEY_RIGHT) {
            if (this.gridApi.getEditingCells().length > 0) {
                const vv = this._getNextEditCellFields(param.colDef.field)
                if (vv) {
                    param.event.stopPropagation()
                    param.event.preventDefault()
                    this.gridApi.setFocusedCell(param.rowIndex, vv)
                    this.gridApi.stopEditing()
                    this.gridApi.startEditingCell({
                        rowIndex: param.rowIndex,
                        colKey: vv
                    })
                }
            }
        } else if (param.event.keyCode === KEY_TAB) {
            if (param.event.shiftKey === true) {
                const vv = this._getPreviousEditCellFields(param.colDef.field)
                if (!vv) {  // 如果是第一列
                    const num = param.rowIndex - 1
                    if (num >= 0) {
                        this.singleSelectRow(node => node.rowIndex === num)
                    }
                }
            } else {
                const vv = this._getNextEditCellFields(param.colDef.field)
                if (!vv) {  // 如果是最后一列
                    const num = param.rowIndex + 1
                    if (num >= this._data.length) {
                        if (this.props.vjson.editable) {
                            param.event.stopPropagation()
                            param.event.preventDefault()
                            this.createNewRow()
                            const firstField = this._getFirstEditCellFields();
                            this.gridApi.ensureColumnVisible(firstField)
                            this.gridApi.setFocusedCell(num, firstField)
                            this.gridApi.startEditingCell({
                                rowIndex: num,
                                colKey: firstField
                            })
                        }
                    }
                    this.singleSelectRow(node => node.rowIndex === num)
                }
            }
        }
    }

    _getNextEditCellFields(field: string) {
        const nn = _.indexOf(this.gridEditCells, field);
        if (nn == -1 && nn + 1 >= this.gridEditCells.length) {
            return null
        }
        return this.gridEditCells[nn + 1]
    }

    _getPreviousEditCellFields(field: string) {
        const nn = _.indexOf(this.gridEditCells, field);
        if (nn == -1 && nn - 1 < 0) {
            return null
        }
        return this.gridEditCells[nn - 1]
    }

    get gridEditCells() {
        if (!this._gridEditCells) {
            const vv: any[] = []
            _.forEach(this.props.vjson.columns, item => {
                if (item.editable) {
                    vv.push(item.field)
                }
            })
            this._gridEditCells = vv
        }
        return this._gridEditCells
    }

    _getFirstEditCellFields() {
        if (this.gridEditCells.length >= 0) {
            return this.gridEditCells[0]
        }
        return null
    }

    _buildEventParams(e: any) {
        if (typeof this._checkboxColumn === 'string' && this._checkboxColumn) {
            e.toggle = () => {
                if (!e.data) {
                    return
                }
                const id = this._getIdByRow(e.data)
                const scope = this.props.$context.scopeInstance
                const checkedData = _.get(scope, this._checkboxColumn!)

                const idx = _.findIndex(checkedData, (r) => this._getIdByRow(r) === id)
                if (idx < 0) {
                    checkedData.push(e.data)
                } else {
                    _.remove(checkedData, (r) => this._getIdByRow(r) === id)
                }
            }
        }

        return e
    }

    _rowDoubleClicked(e: any) {
        return YvanEventDispatch(this.props.vjson.onRowDblClick, this, this._buildEventParams(e))
    }

    _onCellClicked(e: any) {
        return YvanEventDispatch(this.props.vjson.onCellClicked, this, this._buildEventParams(e))
    }

    private _onGridReady(gridReady: any) {
        this.gridApi = gridReady.api
        this.columnApi = gridReady.columnApi
        this.addColWidthChange()
        YvanEventDispatch(this.props.vjson.onGridReady, this)

        // @ts-ignore
        const $el = $(this.gridApi.gridPanel?.eBodyViewport)
        if ($el) {
            // 让表格可复制
            $el.closest('.ag-unselectable').removeClass('ag-unselectable')
        }

        if (this._isFirstRender) {
            this._isFirstRender = false

            if (this.props.vjson.bindSelected) {
                this._bindSelectDisposer = mobx.reaction(
                    () => this.dataVersion.get(),
                    () => {
                        let row = this.getSelectedRow()
                        if (typeof row === 'undefined') {
                            row = null
                        }
                        const scope = this.props.$context.scopeInstance
                        _.set(scope, this.props.vjson.bindSelected, row)

                        //触发 onRowSelect 事件
                        YvanEventDispatch(this.props.vjson.onRowSelect, this, row)
                    })
            }

            // 只在第一次 Ready 时执行以下代码
            _.some(this.columnApi.getAllColumns(), column => {
                // @ts-ignore
                const {view, bind} = column.getColDef()
                if (view === 'checkbox') {
                    if (!_.has(this.props.$context.scopeInstance, bind)) {
                        _.set(this.props.$context.scopeInstance, bind, [])
                    }
                    this._checkboxColumn = bind
                    return true
                }
            })

            if (this.props.vjson.dataSource && this.props.vjson.autoDataSource) {
                this.refresh();
            }

            // 自动焦点跳转
            if (_.isNumber(this.props.vjson.ff)) {
                setTimeout(() => {
                    console.log('gridApi', this.grid, this.gridApi)
                    this.gridApi.setFocusedCell(0, '__ID__')

                }, this.props.vjson.ff)
            }

            // 双向绑定列的隐藏设置
            mobx.autorun(() => {
                _.forOwn(this.mobxState.columnHiddenSetting, (v, k) => {
                    this.columnApi.setColumnVisible(k, !v)
                })
            })
        }
    }

    /**=========================== 表格列隐藏方法 ======================== **/
    public getColumnHiddenSetting() {
        return this.mobxState.columnHiddenSetting;
    }

    public setColumnHiddenSetting(columnHiddenSetting) {
        this.mobxState.columnHiddenSetting = columnHiddenSetting;
    }

    /**=========================== 表格导出相关 ======================== **/

    private getExportDataPromise: any = null;
    private exportDataPromise: any = null;

    getExportData(): Promise<any> {

        if (this.exportDataPromise == null) {
            this.refresh({isExportData: true});
            // this.loading = true;
            this.exportDataPromise = new Promise<any>(resolve => {
                this.getExportDataPromise = resolve;
            })
        }
        return this.exportDataPromise
    }

    export() {
        this.refresh({isExportData: true});
        // this.loading = true;
    }

    set exportData(value: any[]) {
        this._exportData = value;
        // 导出数据
        if (value && value.length > 0) {
            this.exportExcel(value);
        }
    }

    exportExcel(exportData: any) {
        // 获取表格显示的列
        const colsDefKeys: any[] = []
        const colsDefTitles: any[] = []
        // const colsDictData: any[] = []
        const colsFormat: any[] = []
        // const temp = this.columnApi.getAllDisplayedColumns();
        const temp = this.columnApi.getAllColumns();
        for (let i = 0; i < temp.length; i++) {
            const it = temp[i]['colDef'];
            if (!it['field'] || it['field'] === '__ID__') {
                continue
            }
            // 隐藏字段没有标记需要导出就不导出
            if ((!temp[i].isVisible()) && (!(temp[i].getUserProvidedColDef()['needExport']))) {
                continue
            }
            // 标记了不导出的字段 就不导出
            if (temp[i].getUserProvidedColDef() && (temp[i].getUserProvidedColDef()['needExport'] === false)) {
                continue
            }
            colsDefKeys.push(it['field']);
            colsDefTitles.push(it['headerName'] || it['field']);
            // colsDictData.push(it['data'])
            colsFormat.push(it['valueFormatter'])
        }

        //数据，一定注意需要时二维数组
        let data: any[][] = [colsDefTitles]
        // 判断数据格式 [[],[],...]
        const fields = exportData[0];
        let indexs: any[] = colsDefKeys;
        if (_.isArray(fields)) {
            // 获取需要导出的数据在 数据源里面的index
            indexs = [];
            for (let i = 0; i < colsDefKeys.length; i++) {
                const ck = colsDefKeys[i];
                let finded = false;
                for (let j = 0; j < fields.length; j++) {
                    if (ck === fields[j]) {
                        indexs.push(j);
                        finded = true;
                        break
                    }
                }
                if (!finded) {
                    indexs.push(-1);
                }
            }
            exportData.splice(0, 1);
        }

        // 转换数据
        for (let i = 0; i < exportData.length; i++) {
            const el = exportData[i];
            const newEl: any[] = [];
            for (let j = 0; j < indexs.length; j++) {
                if ((typeof indexs[j] === 'number' && indexs[j] >= 0 || indexs[j]) && el[indexs[j]]) {
                    let iv = el[indexs[j]];

                    // 格式化 （包括字典）
                    if (colsFormat[j]) {
                        const ft = colsFormat[j];
                        if (typeof ft === 'function') {
                            iv = ft({data: {value: iv}, value: iv})
                        }
                    }
                    // 字典转换
                    // if (colsDictData[j]) {
                    //     const dt = colsDictData[j];
                    //     if (_.isArray(dt) && dt.length > 0) {
                    //         _.forEach(dt, (v)=>{
                    //             if (''+v.id === ''+iv) {
                    //                 iv = v.text;
                    //                 return
                    //             }
                    //         })
                    //     } else {
                    //         const nv = dt[iv]
                    //         if ( nv && nv.length > 0) {
                    //             iv = nv;
                    //         }
                    //     }
                    // }

                    newEl.push(iv);
                } else {
                    newEl.push("");
                }
            }
            data.push(newEl);
        }

        const filename = this.props.vjson.exportExcelName; //文件名称
        // var ws_name = "Sheet1"; //Excel第一个sheet的名称

        if (this.props.vjson.onExportExcelStyle) {
            data = YvanEventDispatch(this.props.vjson.onExportExcelStyle, this, data);
        }

        for (let index = 0; index < data.length; index++) {
            const vv = data[index];
            const row: any[] = [];
            for (let i = 0; i < vv.length; i++) {
                row.push({
                    key: colsDefKeys[i],
                    value: vv[i],
                    cellIndex: i
                })
            }
            for (let i = 0; i < vv.length; i++) {

                if (this.props.vjson.onExportExcelCellBgColor) {
                    const cell = {
                        key: colsDefKeys[i],
                        value: vv[i],
                        cellIndex: i
                    }
                    const color = YvanEventDispatch(this.props.vjson.onExportExcelCellBgColor, this, cell, row, index === 0)
                    if (color.length > 0) {
                        vv[i] = {
                            v: vv[i],
                            s: {
                                fill: {bgColor: {indexed: 64}, fgColor: {rgb: color}},
                                // alignment: {
                                //     horizontal: 'center',
                                //     vertical: 'center'
                                // }
                            }
                        }
                    }
                }
            }
        }

        if (this.props.vjson.onBeforeExport) {
            data = YvanEventDispatch(this.props.vjson.onBeforeExport, this, data);
        }
        if (this.getExportDataPromise) {
            const sheetName = filename.split('.')[0];
            const ed = {};
            ed[sheetName] = data;
            this.getExportDataPromise(ed);
            this.getExportDataPromise = null;
            this.exportDataPromise = null;
        } else {
            XLSX_EXPORT.exportExcel(data, filename, 'xlsx')
        }
        this.loading = false;
    }

    /**=========================== 表格列宽 ======================== **/

    private colWidthChangeFlag: number = -1;
    private idColWidth: number | undefined = undefined;

    addColWidthChange() {
        if (!this.props.vjson.notSaveColSetting) {
            _.forEach(this.columnApi.getAllDisplayedColumns(), column => {
                column.addEventListener('widthChanged', this.onWidthChange.bind(this))
            })
        }
    }

    onWidthChange() {
        window.clearTimeout(this.colWidthChangeFlag);
        this.colWidthChangeFlag = window.setTimeout(this.saveColWidth.bind(this), 1000);
    }

    saveColWidth() {
        let key = window.location.href;
        let columnStr = this.props.vjson.ref || '';
        const widthData: any = {cols: []};
        _.forEach(this.props.vjson.columns, column => {
            if (!column.hidden) {
                const colId = column.field || column.title || ''
                columnStr = columnStr + colId + '-';
            }
        })
        _.forEach(this.columnApi.getAllDisplayedColumns(), column => {
            const colDef = column.getColDef();
            const colId = colDef.field || colDef.headerName || '';
            const width = column.getActualWidth();
            if (colId === '__ID__') {
                widthData['__ID__'] = width
            } else {
                widthData.cols.push({colId, width})
            }
        })
        key = key + columnStr;
        localStorage.setItem(key, JSON.stringify(widthData));
    }

    mergeLocalColWidth(props) {
        let key = window.location.href;
        let columnStr = props.vjson.ref || '';
        _.forEach(props.vjson.columns, column => {
            if (!column.hidden) {
                const colId = column.field || column.title || ''
                columnStr = columnStr + colId + '-';
            }
        })
        key = key + columnStr;
        const colDataStr = localStorage.getItem(key);
        if (colDataStr) {
            const colData: WidthData = JSON.parse(colDataStr);
            this.idColWidth = colData.__ID__;
            let len = colData.cols.length;
            const len1 = props.vjson.columns?.length || 0;
            len = len <= len1 ? len : len1;
            for (let i = 0, j = 0; i < len;) {
                if (j >= len1) {
                    break;
                }
                const cdel = colData.cols[i];
                // @ts-ignore
                const cvel = props.vjson.columns[j]
                const colId = cvel.field || cvel.title || ''
                if (cdel.colId === colId) {
                    // 不取保存列宽
                    if (cvel.notSaveWith === true) {

                    } else {
                        cvel.width = cdel.width;
                    }
                    // _.set(props.vjson, 'columns['+j+'].width', cdel.width);
                    i++, j++
                } else {
                    j++
                }
            }
        }
    }

    /**=========================== 表格分页相关 ======================== **/

    get currentPage() {
        return this.mobxState.currentPage
    }

    get pageSize() {
        return this.mobxState.pageSize
    }

    private _onPageChange(currentPage: number, pageSize?: number) {
        if (pageSize && pageSize !== this.mobxState.pageSize) {
            this.mobxState.pageSize = pageSize
            this.mobxState.currentPage = 1
            this.mobxState.needCount = true;
        } else {
            this.mobxState.currentPage = currentPage
            this.mobxState.needCount = false;
        }
        this.refresh({resetPage: false});
    }

    private _renderPagination() {
        if (!this.props.vjson.pagination) {
            return null
        }

        return (
            <div className="yvan-grid-pagination">
                <AntdPagination
                    total={this.mobxState.total}
                    defaultPageSize={this.mobxState.pageSize}
                    // onShowSizeChange={this.onShowSizeChange.bind(this)}
                    current={this.mobxState.currentPage}
                    onChange={this._onPageChange.bind(this)}
                    {
                        ...this.props.vjson.paginationOption
                    }
                />
            </div>
        )
    }

    fullScreen() {
        this.props.vjson.fullscreen = !this.props.vjson.fullscreen
    }

    stopEditing() {
        this.gridApi.stopEditing();
    }

    /**
     * 勾选全部行
     */
    checkAll() {
        const scope = this.props.$context.scopeInstance
        const checkedData: any[] = []

        this.forEachData(data => {
            checkedData.push(data)
        })

        _.set(scope, this._checkboxColumn!, checkedData)
    }

    /**
     * 不勾选任何行
     */
    uncheckAll() {
        const scope = this.props.$context.scopeInstance
        _.set(scope, this._checkboxColumn!, [])
    }

    autoSizeColumnsToFit() {
        this.gridApi.sizeColumnsToFit()
    }

    private _autoSizeSkipHeader: boolean = false

    autoSizeColumns() {
        this._autoSizeSkipHeader = !this._autoSizeSkipHeader
        this.columnApi.autoSizeAllColumns(this._autoSizeSkipHeader)
    }

    columnHiddenChange(col: any, value: any) {
        const {field} = col
        this.mobxState.columnHiddenSetting[field] = !value.target.checked
    }

    columnHiddenReset(event: any) {
        mobx.transaction(() => {
            _.forEach(this.props.vjson.columns, (col) => {
                if (!col.hidden) {
                    this.mobxState.columnHiddenSetting[col.field] = false
                }
            })
        })
    }

    columnHiddenUncheckAll(event: any) {
        mobx.transaction(() => {
            _.forEach(this.props.vjson.columns, (col) => {
                // if (!col.hidden) {
                this.mobxState.columnHiddenSetting[col.field] = true
                // }
            })
        })
    }

    columnHiddenSaveAsDefault(event: any) {

        let key = window.location.href;
        let columnStr = this.props.vjson.ref || '';
        _.forEach(this.props.vjson.columns, column => {
            if (!column.hidden) {
                const colId = column.field || column.title || ''
                columnStr = columnStr + colId + '-';
            }
        })

        key = key + columnStr + "_col_hidden";
        localStorage.setItem(key, JSON.stringify(this.mobxState.columnHiddenSetting));
        showInfo("已经保存为默认配置")
    }

    getSavedColumnHidden() {

        let key = window.location.href;
        let columnStr = this.props.vjson.ref || '';
        _.forEach(this.props.vjson.columns, column => {
            if (!column.hidden) {
                const colId = column.field || column.title || ''
                columnStr = columnStr + colId + '-';
            }
        })
        key = key + columnStr + "_col_hidden";
        const colHiddenStr = localStorage.getItem(key);
        if (colHiddenStr) {
            const colHiddenData: WidthData = JSON.parse(colHiddenStr);
            // _.assign(this.mobxState.columnHiddenSetting, colHiddenData);
            return colHiddenData;
        }
        return {}
    }

    getColumnHiddenMenu() {
        _.map(this.props.vjson.columns, (col) => {
            if (col.hidden)
                return null
            else
                return this.mobxState.columnHiddenSetting[col.field]
        })

        return (
            <Menu>
                {
                    _.map(this.props.vjson.columns, (col) => {
                        if (!col.field || col.hidden)
                            return null
                        else
                            return (
                                <Menu.Item key={_.uniqueId("_")}>
                                    <Checkbox key={col.field} name={col.field}
                                              checked={!this.mobxState.columnHiddenSetting[col.field]}
                                              onChange={(v) => this.columnHiddenChange(col, v)}
                                    >{col.title}</Checkbox>
                                </Menu.Item>
                            )
                    })
                }
                <Menu.Divider key={_.uniqueId("_")}/>
                <Menu.Item key={_.uniqueId("_")}>
                    <Button type="link" onClick={(t) => this.columnHiddenReset(t)}>全选</Button>
                    <Button type="link" onClick={(t) => this.columnHiddenUncheckAll(t)}>全不选</Button>
                    <Button type="link" onClick={(t) => this.columnHiddenSaveAsDefault(t)}>保存为默认</Button>
                </Menu.Item>
            </Menu>
        )
    }

    handleColumnHiddenMenuVisibleChange() {
        this.setState({
            columnHiddenMenuVisible: !this.state.columnHiddenMenuVisible
        })
    }

    setupGrid(grid: any) {
        if (!grid) {
            // @ts-ignore
            this.gridApi = undefined
            // @ts-ignore
            this.columnApi = undefined
        } else {
            this.gridApi = grid.api
            this.columnApi = grid.columnApi
            grid.gridOptions.api.vue = this
        }
    }

    private _renderCheckbox() {
        if (!this._checkboxColumn) {
            return null
        }

        const scope = this.props.$context.scopeInstance
        const count = _.size(_.get(scope, this._checkboxColumn))
        if (!count) {
            return null
        }

        return (
            <Alert style={{minWidth: '200px'}}
                   type="info"
                   message={`已选择 ${count} 项`}
                   onClose={() => _.get(scope, this._checkboxColumn!).splice(0, count)}
                   closeText="取消选择"/>
        )
    }

    render() {
        if (Extend.isDebugMode()) {
            console.log('RenderGrid ', this.toString())
        }

        const wrapperStyle: CSSProperties = {
            ...mobx.toJS(this.props.vjson.style)
        }
        if (this.props.vjson.height === 'flex') {
            wrapperStyle.flexGrow = 1
        } else {
            wrapperStyle.height = this.props.vjson.height
        }

        return (
            // @ts-ignore
            <div key={this.key} view_id={this.key} aria-roledescription={this.view}
                 style={wrapperStyle}
                 className={"yvan-grid-wrapper" + (this.props.vjson.fullscreen ? ' fullscreen' : '') + ' ' + (this.props.vjson.className)}>
                {
                    this.props.vjson.hideToolbar
                        ? null
                        : <div className="yvan-grid-toolbar"
                               style={mobx.toJS(this.props.vjson.toolbarStyle)}>
                            <div className="yvan-grid-toolbar-container">
                                <div className="yvan-grid-toolbar-left">
                                    {
                                        this.props.vjson.title ?
                                            <div className="yvan-grid-toolbar-title">{this.props.vjson.title}</div>
                                            : null
                                    }
                                    {
                                        this._renderCheckbox()
                                    }
                                </div>
                                <div className="yvan-grid-toolbar-right">
                                    <Space>
                                        {
                                            ...renderItems(this, this.props.vjson.toolbar)
                                        }
                                    </Space>

                                    {
                                        this.props.vjson.hideToolbarDivider ? null
                                            : <div className="yvan-grid-toolbar-divider">
                                                <Divider type="vertical"/>
                                            </div>
                                    }

                                    {
                                        this.props.vjson.hideRefreshButton ? null
                                            : <div className="yvan-grid-toolbar-setting-item">
                                                <Button type="dashed" shape="circle" size="large" title="刷新"
                                                        icon={<ReloadOutlined/>}
                                                        onClick={this.refreshData.bind(this)}/>
                                            </div>
                                    }

                                    {
                                        this.props.vjson.filterable ?
                                            <div className="yvan-grid-toolbar-setting-item">
                                                <Button type="dashed" shape="circle" size="large" title="清空筛选"
                                                        icon={<FunnelPlotOutlined/>}
                                                        onClick={this.refreshData.bind(this)}/>
                                            </div>
                                            : null
                                    }

                                    {
                                        this.props.vjson.hideExportButton ? null
                                            : <div className="yvan-grid-toolbar-setting-item">
                                                <Button type="dashed" shape="circle" size="large" title="导出Excel"
                                                        icon={<DownloadOutlined/>}
                                                        onClick={this.export.bind(this)}/>
                                            </div>
                                    }

                                    {
                                        this.props.vjson.hideAutoFitButton ? null
                                            : <div className="yvan-grid-toolbar-setting-item">
                                                <Button type="dashed" shape="circle" size="large" title="调整到最合适列宽"
                                                        icon={<MergeCellsOutlined/>}
                                                        onClick={this.autoSizeColumnsToFit.bind(this)}/>
                                            </div>
                                    }

                                    {
                                        this.props.vjson.hideAutoSizeButton ? null
                                            : <div className="yvan-grid-toolbar-setting-item">
                                                <Button type="dashed" shape="circle" size="large" title="根据内容调整到最大列宽"
                                                        icon={<ColumnWidthOutlined/>}
                                                        onClick={this.autoSizeColumns.bind(this)}/>
                                            </div>
                                    }

                                    {
                                        this.props.vjson.hideColumnSettingButton ? null
                                            : <div className="yvan-grid-toolbar-setting-item">
                                                <Dropdown overlay={this.getColumnHiddenMenu()}
                                                          trigger={['click']}
                                                          onVisibleChange={this.handleColumnHiddenMenuVisibleChange.bind(this)}
                                                          visible={this.state.columnHiddenMenuVisible}
                                                          placement="bottomRight"
                                                >
                                                    <Button type={this.state.columnHiddenMenuVisible ? "primary" : "dashed"}
                                                            shape="circle"
                                                            size="large"
                                                            title="列设置"
                                                            icon={<DownSquareOutlined/>}>
                                                    </Button>
                                                </Dropdown>
                                            </div>
                                    }

                                    {
                                        this.props.vjson.hideFullScreenButton ? null
                                            : <div className="yvan-grid-toolbar-setting-item">
                                                <Button type={this.props.vjson.fullscreen ? "primary" : "dashed"}
                                                        shape="circle"
                                                        size="large"
                                                        title="全屏"
                                                        icon={this.props.vjson.fullscreen ? <FullscreenExitOutlined/> :
                                                            <FullscreenOutlined/>}
                                                        onClick={this.fullScreen.bind(this)}/>
                                            </div>
                                    }
                                </div>
                            </div>
                        </div>
                }
                {/*@ts-ignore*/}
                <div className="yvan-grid ag-theme-alpine" copytag={"ag-grid"} id={this.key}
                     style={mobx.toJS(this.props.vjson.gridStyle)}>
                    <AgGridReact
                        ref={this.setupGrid.bind(this)}
                        gridOptions={this._gridOptionsResult}
                    />
                </div>
                {
                    this._renderPagination()
                }
            </div>
        )

    }
}